<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ScrollText</title>

    <script>
        function createScrollText(str) {
            return new ScrollText(str);
        }

        class ScrollText {
            constructor(str) {
                this.raw = str;
                const text = $('div');
                text.textContent = str;
                this.text = text;
                return this;
            }

            /**
             * @param {string|HTMLElement} sel
             */
            mount(sel) {
                if (this.mounted) return;
                /**@type {HTMLElement} */
                const container = typeof sel === 'string' ? document.querySelector(sel) : sel;
                container.style.setProperty('white-space', 'pre');
                container.style.setProperty('overflow', 'hidden');
                container.append(this.text);
                this.mounted = true;

                this.container = container;
                this.containerWidth = container.clientWidth;
                this.textWidth = measure(this.text);

                return this;
            }

            /**
             * @param pps pixels per second
             */
            animate(pps = 32) {
                if (this.textWidth > this.containerWidth) {
                    // do seamless
                    this.container.style.setProperty('--start', 0);
                    const space = ' '.repeat(10);
                    this.text.textContent = this.raw + space + this.raw;
                    const newWidth = measure(this.text);
                    // [textWidth + spaceWidth + textWidth] = newWidth
                    // when you meet text again, that's the timing to repeat animation
                    const endPoint = newWidth - this.textWidth;
                    this.container.style.setProperty('--end', 0 - endPoint + 'px');
                    const duration = (endPoint) / pps
                    this.text.style.animation = `${duration}s linear infinite scroll-text`;
                }
            }
        }

        function $(tag) {
            return document.createElement(tag);
        }

        /**
         * @param {HTMLElement} text
         */
        function measure(text) {
            text.style.position = 'absolute';
            const width = text.clientWidth;
            text.style.position = '';
            return width;
        }
    </script>

    <style>
        @keyframes scroll-text {
            from {
                transform: translateX(var(--start));
            }

            to {
                transform: translateX(var(--end));
            }
        }
    </style>
</head>

<body>
    <style>
        #seamless {
            width: 240px;
            border: 1px solid firebrick;
        }
    </style>
    <div id="app">
        <h1>
            seamless
        </h1>
        <div id="seamless"></div>
    </div>

    <script>
        const text = 'Seamless scrolling for long text: you know this is long.';
        createScrollText(text).mount('#seamless').animate();

    </script>
</body>

</html>